#include "Unit.h"
#include "FormatOutput.h"
#include <iostream>
#include <sstream>

#define BASEUNIT(seq) (seq.at(seq.size()-1))

namespace
{

void unitAppendToSeq(UnitSeq& unitSeq_i, Unit& unit_i)
{
    unitSeq_i.push_back(&unit_i);
}


}


Unit::Unit(const string& name_i, const int& ratioToBase_i):
    name_m(name_i),
    ratioToBase_m(ratioToBase_i)
{
}

int Unit::toBaseUnitValue(const int& value_i) const
{
    return (value_i * ratioToBase_m);
}

int Unit::getValue() const
{
    return ratioToBase_m;
}

string Unit::getName() const
{
    return name_m;
}

void Unit::eachOutput(ostream& os_io, const int& baseValue_i, void (*print)(ostream&, int&, Unit&, bool&)) 
{
    int value = baseValue_i;
    UnitSeq seq = getUnitSeq();
    
    bool addNull = false;
    for (int i = 0; i < seq.size(); i ++)
        (*print)(os_io, value, *seq.at(i), addNull);
}

void Unit::zeroOutput(ostream& os_io, void (*print)(ostream&, Unit&))
{
    UnitSeq seq = getUnitSeq();
    (*print)(os_io, *BASEUNIT(seq)); 
}

void Unit::baseOutput(ostream& os_io, const int& baseValue_i, void (*print)(ostream&, const int&, Unit&))
{
    UnitSeq seq = getUnitSeq();
    (*print)(os_io, baseValue_i, *BASEUNIT(seq));
}



/////////////////////////////////////////////////////////////
/////////////// LenUnit ////////////////////////////////////

#define RATIO_FEET_TO_BASE     1
#define RATIO_YARD_TO_BASE     3*RATIO_FEET_TO_BASE
#define RATIO_MILE_TO_BASE     1760*RATIO_YARD_TO_BASE

LenUnit::LenUnit(const string& name_i, const int& ratioToBase_i):
    Unit(name_i, ratioToBase_i)
{
}

LenUnit& LenUnit::getMile()
{

    static LenUnit mile("mile", RATIO_MILE_TO_BASE);
    return mile;
}

LenUnit& LenUnit::getYard()
{
    static LenUnit yard("yard", RATIO_YARD_TO_BASE);
    return yard;
}

LenUnit& LenUnit::getFeet()
{
    static LenUnit feet("feet", RATIO_FEET_TO_BASE);
    return feet;
}

LenUnit& LenUnit::getBaseUnit()
{
    return FEET;
}


UnitSeq& LenUnit::getUnitSeq() 
{
    static UnitSeq lenUnitSeq;
    
    if(lenUnitSeq.empty())
    {
        unitAppendToSeq(lenUnitSeq, MILE);
        unitAppendToSeq(lenUnitSeq, YARD);
        unitAppendToSeq(lenUnitSeq, FEET);
        
        //unitSeqSort(lenUnitSeq);
    }

    return lenUnitSeq;
}

/////////////////////////////////////////////////////////////
/////////////// CubUnit ////////////////////////////////////

#define RATIO_TSP_TO_BASE     1
#define RATIO_TBSP_TO_BASE    3*RATIO_TSP_TO_BASE
#define RATIO_OZ_TO_BASE      2*RATIO_TBSP_TO_BASE

CubUnit::CubUnit(const string& name_i, const int& ratioToBase_i):
    Unit(name_i, ratioToBase_i)
{
}

CubUnit& CubUnit::getOZ()
{

    static CubUnit oz("oz", RATIO_OZ_TO_BASE);
    return oz;
}

CubUnit& CubUnit::getTBSP()
{
    static CubUnit tbsp("tbsp", RATIO_TBSP_TO_BASE);
    return tbsp;
}

CubUnit& CubUnit::getTSP()
{
    static CubUnit tsp("tsp", RATIO_TSP_TO_BASE);
    return tsp;
}

CubUnit& CubUnit::getBaseUnit()
{
    return TSP;
}


UnitSeq& CubUnit::getUnitSeq()
{
    static UnitSeq cubUnitSeq;

    if(cubUnitSeq.empty())
    {
        unitAppendToSeq(cubUnitSeq, OZ);
        unitAppendToSeq(cubUnitSeq, TBSP);
        unitAppendToSeq(cubUnitSeq, TSP);

        //unitSeqSort(cubUnitSeq);
    }

    return cubUnitSeq;
}

